#pragma once

#include <type_traits>
#include <utility>
#include <tuple>

/*

unique_tuple<T...>, where types in <T...> may themselve be a unqiue_tupe<...>,
contains exactly one copy of each of the "deepest nested" element types in <T...>,
regardless of how many times a type-name appears in the type parameter pack,
or nested inside one or more type in <T...> which are themselves unique_type<...>s.

unique_tuple<...> in a way emulates virtual inheritance, but at compile time.
The implementation does NOT use virtual inheritance.

In the following example, t1,t2,t3 all contain exactly one A, and exaclty one B:

   struct A{};
   struct B{};
   
   unique_tuple<A,B,A,B> t1(A{},B{});
   unique_tuple<A,unique_tuple<B>,unique_tuple<A,B>> t2{B{},A{}}; // order doesn't matter in constructor
   
   // B is default constructed, empty unique_tuple (possibly nested) doesn't matter
   unique_tuple<A,unique_tuple<unique_tuple<>>,unique_tuple<A,B,unique_tuple<A,B,unique_tuple<A,B>>>> t3{A{}};
 
   // unique_tuple<A,B> t4(A{},B{},A{}); // doesn't compile because more than one A is supplied
   // unique_tuple<A,B> t5(A{},B{},1.0); // doesn't compile because double doesn't belong

There are helper classes and functions, e.g.:
 
   A& a = get<A>(t0);
 
   auto x = make_unique_tuple(A{},B{},1,1.0); // unique_tuple<A,B,int,double>
 
   static_assert( is_unique_tuple<decltype(t0)>::value, "t0 is a unique_tuple type" );
   static_assert( is_element_of<A,decltype(t1)>::value, "A is an element in t1" );
   static_assert( is_subset_of<decltype(t1),decltype(x)>::value, "<A,B> is a subset of <A,B,int,double>" );
   static_assert( unique_tuple_size<decltype(t3)>::value == 2, "there are 2 elements in t3: A and B" );

Equality comparason exists for two unique_tuple<...>s that contain the same element types, e.g.,
   t0 == t2 && t1 != t3;

Mixed operation with std::tuple is available if std::get<typename>(std::tuple<...>) exists (c++14), which
effectively requires that the element types are all unique in the std::tuple<...>.


synopsis:

template < typename... T >
struct unique_tuple
{
   template < typename... U > explicit constexpr unique_tuple(U&&...);
   template < typename... U > explicit constexpr unique_tuple(const std::tuple<U...>&);
   template < typename... U > explicit constexpr unique_tuple(std::tuple<U...>&&);

   // allocator extended constructors
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, U&&...);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, const unique_tuple<U...>&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, unique_tuple<U...>&&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, const std::tuple<U...>&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, std::tuple<U...>&&);
   
   // assign
   template < typename... U > unique_tuple& operator=(const unique_tuple<U...>&);
   template < typename... U > unique_tuple& operator=(unique_tuple<U...>&&);
   template < typename... U > unique_tuple& operator=(const std::tuple<U...>&);
   template < typename... U > unique_tuple& operator=(std::tuple<U...>&&);
   
   // swap
   template < typename... U > void swap(unique_tuple<U...>&);
   template < typename... U > void swap(std::tuple<U...>&);
};

// helpers
template < typename... T > unique_tuple<typename std::decay<T>::type...> make_unique_tuple(T&&... t);

template < typename... T > struct unique_tuple_size<unique_tuple<T...>>; // number of unique elements
template < typename T > struct is_unique_tuple;
template < typename T, typename... U > struct is_element_of<T,unique_tuple<U...>>;
template < typename... T, typename... U > struct is_subset_of<unique_tuple<T...>,unique_tuple<U...>>;

// element access
template < typename T, typename... U > T& get<T>(unique_tuple<U...>&);
template < typename T, typename... U > const T& get<T>(const unique_tuple<U...>&);
template < typename T, typename... U > T&& get<T>(unique_tuple<U...>&&);

// equality compare
template < typename... T, typename... U > bool operator==(const unique_tuple<T...>&, const unique_tuple<U...>&);
template < typename... T, typename... U > bool operator==(const unique_tuple<T...>&, const std::tuple<U...>&);
template < typename... T, typename... U > bool operator==(const std::tuple<T...>&, const unique_tuple<U...>&);

// swap
template < typename... T, typename... U > void swap(unique_tuple<T...>&, unique_tuple<U...>&);
template < typename... T, typename... U > void swap(unique_tuple<T...>&, std::tuple<U...>&);
template < typename... T, typename... U > void swap(std::tuple<T...>&, unique_tuple<U...>&);

*/

// forward declare
template < typename... > struct unique_tuple;
template < typename > struct unique_tuple_size;
template < typename > struct is_unique_tuple;
template < typename, typename > struct is_element_of;
template < typename, typename > struct is_subset_of;

// un-cvref helper
template < typename T >
using __uncvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

// unique tuple test
template < typename > struct is_unique_tuple : std::false_type {};
template < typename... T > struct is_unique_tuple<unique_tuple<T...>> : std::true_type {};

// empty test
template < typename > struct is_empty_unique_tuple : std::false_type {};
template <> struct is_empty_unique_tuple<unique_tuple<>> : std::true_type {};
template < typename T, typename... U >
struct is_empty_unique_tuple<unique_tuple<T,U...>>
 : std::integral_constant<
      bool,
      is_empty_unique_tuple<T>::value && is_empty_unique_tuple<unique_tuple<U...>>::value
   >
{};

// element test
template < typename, typename > struct is_element_of
 : std::false_type {};

template < typename T > struct is_element_of<T,unique_tuple<>> : std::false_type {};

template < typename T, typename... U, typename... V >
struct is_element_of<T,unique_tuple<unique_tuple<U...>,V...>>
 : is_element_of<T,unique_tuple<U...,V...>>
{};

template < typename T, typename... U >
struct is_element_of<T,unique_tuple<unique_tuple<U...>>>
 : is_element_of<T,unique_tuple<U...>>
{};

template < typename T, typename U, typename... V >
struct is_element_of<T,unique_tuple<U,V...>>
 : std::integral_constant<
      bool,
      std::is_same<T,U>::value || is_element_of<T,unique_tuple<V...>>::value
   >
{};

// subset test
template < typename... T >
struct is_subset_of<unique_tuple<>,unique_tuple<T...>> : std::true_type {};

template < typename T, typename... U >
struct is_subset_of<T,unique_tuple<U...>> : std::false_type {};

template < typename T, typename... U >
struct is_subset_of<unique_tuple<T>,unique_tuple<U...>>
 : std::conditional<
      is_unique_tuple<T>::value,
      is_subset_of<T,unique_tuple<U...>>,
      is_element_of<T,unique_tuple<U...>>
   >::type
{};

template < typename T, typename... U, typename... V >
struct is_subset_of<unique_tuple<T,U...>,unique_tuple<V...>>
 : std::integral_constant<
      bool,
      is_subset_of<unique_tuple<T>,unique_tuple<V...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple<V...>>::value
   >
{};

// tuple size
template <> struct unique_tuple_size<unique_tuple<>> : std::integral_constant<std::size_t,0u> {};

template < typename... T, typename... U >
struct unique_tuple_size<unique_tuple<unique_tuple<T...>,U...>>
 : unique_tuple_size<unique_tuple<T...,U...>> {};

template < typename... T >
struct unique_tuple_size<unique_tuple<unique_tuple<T...>>>
 : unique_tuple_size<unique_tuple<T...>> {};

template < typename T >
struct unique_tuple_size<unique_tuple<T>>
 : std::conditional<
      is_unique_tuple<T>::value,
      unique_tuple_size<T>,
      std::integral_constant<std::size_t,1u>
   >::type
{};

template < typename T, typename... U >
struct unique_tuple_size<unique_tuple<T,U...>>
 : std::conditional<
      is_subset_of<unique_tuple<T>,unique_tuple<U...>>::value,
      unique_tuple_size<unique_tuple<U...>>,
      std::integral_constant<std::size_t,1u+unique_tuple_size<unique_tuple<U...>>::value>
   >::type
{};

// make unique tuple
template < typename... T >
unique_tuple<typename std::decay<T>::type...>
make_unique_tuple(T&&... t) noexcept
{
   typedef unique_tuple<typename std::decay<T>::type...> result_t;
   return result_t(std::forward<T>(t)...);
}

// generalized get functions, useful for unqiue_tuple's constructor
// recursively defined
template < typename T, typename U, typename... V >
typename std::enable_if<
   !is_unique_tuple<T>::value
   && std::is_same<T,__uncvref<U>>::value,
   U&&
>::type
constexpr get(U&& t, V&&... v) noexcept
{
   using tail_type = unique_tuple<__uncvref<V>...>;
   static_assert(!is_element_of<T,tail_type>::value,"");
   return std::forward<U>(t);
}

template < typename T, typename... U, typename... V >
typename std::enable_if<
   is_element_of<T,unique_tuple<U...>>::value && (sizeof...(V)>0),
   T&&
>::type
constexpr get(unique_tuple<U...>&& t, V&&... v) noexcept
{
   using tail_type = unique_tuple<__uncvref<V>...>;
   static_assert(!is_element_of<T,tail_type>::value,"");
   return std::move(get<T>(t));
}

template < typename T, typename... U, typename... V >
typename std::enable_if<
   is_element_of<T,unique_tuple<U...>>::value && (sizeof...(V)>0),
   const T&
>::type
constexpr get(const unique_tuple<U...>& t, V&&... v) noexcept
{
   using tail_type = unique_tuple<__uncvref<V>...>;
   static_assert(!is_element_of<T,tail_type>::value,"");
   return get<T>(t);
}

template < typename T, typename U, typename... V >
typename std::enable_if<
   !std::is_same<T,__uncvref<U>>::value
   && !is_element_of<T,__uncvref<U>>::value,
   decltype(get<T>(std::forward<V>(std::declval<V>())...))
>::type
constexpr get(U&& t, V&&... v) noexcept
{
   return get<T>(std::forward<V>(v)...);
}

// impl
template < typename... > struct unique_tuple_impl;

// helper
template <typename T, typename... U >
struct unique_tuple_helper_eval_result
 : unique_tuple_impl<T>, unique_tuple_impl<U...>
{
   using head_t = unique_tuple_impl<T>;
   using tail_t = unique_tuple_impl<U...>;
   
   // constructor
   template < typename... V >
   explicit constexpr unique_tuple_helper_eval_result(V&&... v)
   noexcept(
      std::is_nothrow_constructible<unique_tuple<T>,V&&...>::value &&
      std::is_nothrow_constructible<unique_tuple<U...>,V&&...>::value
   )
    : head_t(std::forward<V>(v)...)
    , tail_t(std::forward<V>(v)...)
   {}

   // assign
   template < typename V >
   unique_tuple_helper_eval_result& operator=(const V& v)
   noexcept(
      std::is_nothrow_assignable<head_t,const V&>::value
      && std::is_nothrow_assignable<tail_t,const V&>::value
   )
   {
      static_cast<head_t&>(*this) = v;
      static_cast<tail_t&>(*this) = v;
      return *this;
   }

   template < typename V >
   unique_tuple_helper_eval_result& operator=(V&& v)
   noexcept(
      std::is_nothrow_assignable<head_t,V&&>::value
      && std::is_nothrow_assignable<tail_t,V&&>::value
   )
   {
      static_cast<head_t&>(*this) = v;
      static_cast<tail_t&>(*this) = v;
      return *this;
   }
   
   // equality compare
   template < typename V >
   constexpr bool operator==(const V& v) const noexcept
   {
      return static_cast<const head_t&>(*this) == v
         && static_cast<const tail_t&>(*this) == v;
   }
   
   // swap
   template < typename V >
   void swap(V& v) noexcept(
      noexcept(static_cast<head_t&>(*this).swap(v))
      && noexcept(static_cast<tail_t&>(*this).swap(v))
   )
   {
      static_cast<head_t&>(*this).swap(v);
      static_cast<tail_t&>(*this).swap(v);
   }
};

template <typename T, typename... U >
struct unique_tuple_helper
{
   static_assert(not is_unique_tuple<T>::value,"");
   static_assert(not is_empty_unique_tuple<T>::value,"");
   
   template <
      bool = is_element_of<T,unique_tuple<U...>>::value,
      bool = is_empty_unique_tuple<unique_tuple<U...>>::value,
      typename = void
   >
   struct eval
   {
      using type = unique_tuple_impl<U...>;
   };
   
   template < typename dummy >
   struct eval<false,false,dummy>
   {
      using type = unique_tuple_helper_eval_result<T,U...>;
   };
   
   template < bool b, typename dummy >
   struct eval<b,true,dummy>
   {
      static_assert(!b,"");
      using type = unique_tuple_impl<T>;
   };
};

// 0-element specialization
template <>
struct unique_tuple_impl<>
{
   // constructor
   template < typename... U >
   explicit constexpr unique_tuple_impl(U&&...) noexcept
   {}
   
   template < typename Alloc, typename... U >
   explicit unique_tuple_impl(std::allocator_arg_t, const Alloc& a, U&&...) noexcept
   {}

   // assign
   template < typename U >
   unique_tuple_impl& operator=(const U&) noexcept { return *this; }

   template < typename U >
   unique_tuple_impl& operator=(U&&) noexcept { return *this; }
   
   // equality compare
   template < typename... U,
      typename=typename std::enable_if<is_empty_unique_tuple<U...>::value>::type
   >
   constexpr bool operator==(const unique_tuple<U...>&) const noexcept { return true; }

   template < typename U,
      typename=typename std::enable_if<!is_empty_unique_tuple<U>::value>::type
   >
   constexpr bool operator==(const U&) const noexcept { return false; }

   // swap
   template < typename V >
   void swap(V& v) noexcept {}
};

// 1-element specialization
template < typename T >
struct unique_tuple_impl<T>
{
   static_assert(!is_unique_tuple<T>::value,"");
   
   // get
   template < typename U >
   friend typename std::enable_if<
      std::is_same<T,U>::value,
      T&
   >::type
   constexpr get(unique_tuple_impl& t) noexcept
   {
      return t.t;
   }

   // const get
   template < typename U >
   friend typename std::enable_if<
      std::is_same<T,U>::value,
      const T&
   >::type
   constexpr get(const unique_tuple_impl& t) noexcept
   {
      return t.t;
   }
   
   // move get
   template < typename U >
   friend typename std::enable_if<
      std::is_same<T,U>::value,
      T&&
   >::type
   constexpr get(unique_tuple_impl&& t) noexcept
   {
      return std::move(t.t);
   }
   
   // constructor
   template < typename... U >
   explicit constexpr unique_tuple_impl(U&&...)
   noexcept(std::is_nothrow_default_constructible<T>::value)
    : t() // default contruction
   {}
   
   template < typename Alloc, typename... U >
   explicit constexpr unique_tuple_impl(std::allocator_arg_t, const Alloc& a, U&&...)
    : t(std::allocator_arg_t(),a) // default contruction by allocator
   {}

   // this one is more specialized than the one above for non-zero number of arguments
   // but is enabled only if T is one of, or can be "gotten" from, the arguments
   template < typename U, typename... V,
      typename=decltype(get<T>(
         std::forward<U>(std::declval<U>()),
         std::forward<V>(std::declval<V>())...
      ))
   >
   explicit constexpr unique_tuple_impl(U&& u, V&&... v)
    : t(get<T>(std::forward<U>(u),std::forward<V>(v)...))
   {}
   
   template < typename Alloc, typename U, typename... V,
      typename=decltype(get<T>(
         std::forward<U>(std::declval<U>()),
         std::forward<V>(std::declval<V>())...
      ))
   >
   explicit constexpr unique_tuple_impl(std::allocator_arg_t, const Alloc& a, U&& u, V&&... v)
   noexcept(noexcept(T(get<T>(std::forward<U>(u),std::forward<V>(v)...))))
    : t(std::allocator_arg_t(),a,get<T>(std::forward<U>(u),std::forward<V>(v)...))
   {}

   // assign
   template < typename U >
   unique_tuple_impl& operator=(const U& u)
   noexcept(std::is_nothrow_assignable<T,decltype(get<T>(std::declval<const U&>()))>::value)
   {
      t = get<T>(u);
      return *this;
   }

   template < typename U >
   unique_tuple_impl& operator=(U&& u)
   noexcept(std::is_nothrow_assignable<T,decltype(get<T>(std::declval<U&&>()))>::value)
   {
      t = get<T>(u);
      return *this;
   }
   
   // equality compare
   template < typename U >
   constexpr bool operator==(const U& u) const noexcept
   {
      return t == get<T>(u);
   }
   
   // swap
   template < typename U >
   void swap(U& u) noexcept(noexcept(swap(
      std::declval<std::tuple<T>&>(),
      std::declval<std::tuple<T>&>()
   )))
   {
      using std::swap;
      swap(t,get<T>(u));
   }

private:
   T t;
};

// recursively unwrap unique_tuple<> from the first element
template < typename... T, typename... U >
struct unique_tuple_impl< unique_tuple<T...>, U... >
 : unique_tuple_impl<T...,U...>
{
   using base_t = unique_tuple_impl<T...,U...>;
   using base_t::base_t;

   // assign
   template < typename V >
   unique_tuple_impl& operator=(const V& v)
   noexcept(std::is_nothrow_assignable<base_t,const V&>::value)
   {
      static_cast<base_t&>(*this) = v;
      return *this;
   }

   template < typename V >
   unique_tuple_impl& operator=(V&& v)
   noexcept(std::is_nothrow_assignable<base_t,V&&>::value)
   {
      static_cast<base_t&>(*this) = v;
      return *this;
   }

   // equality compare
   template < typename V >
   constexpr bool operator==(const V& v) const noexcept
   {
      return static_cast<const base_t&>(*this) == v;
   }

   // swap
   template < typename V >
   void swap(V& v)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(v)))
   {
      static_cast<base_t&>(*this).swap(v);
   }
};

// more specialized unwrapping
template < typename... T >
struct unique_tuple_impl<unique_tuple<unique_tuple<T...>>>
 : unique_tuple_impl<T...>
{
   using base_t = unique_tuple_impl<T...>;
   using base_t::base_t;

   // assign
   template < typename U >
   unique_tuple_impl& operator=(const U& u)
   noexcept(std::is_nothrow_assignable<base_t,const U&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   template < typename U >
   unique_tuple_impl& operator=(U&& u)
   noexcept(std::is_nothrow_assignable<base_t,U&&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   // equality compare
   template < typename U >
   constexpr bool operator==(const U& u) const noexcept
   {
      return static_cast<const base_t&>(*this) == u;
   }

   // swap
   template < typename U >
   void swap(U& u)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(u)))
   {
      static_cast<base_t&>(*this).swap(u);
   }
};

template < typename... T >
struct unique_tuple_impl<unique_tuple<T...>>
 : unique_tuple_impl<T...>
{
   using base_t = unique_tuple_impl<T...>;
   
   // inhetrit constructor may be private
   template < typename... V >
   explicit constexpr unique_tuple_impl(V&&... v)
   noexcept(std::is_nothrow_constructible<base_t,V&&...>::value)
    : base_t(std::forward<V>(v)...)
   {}

   // assign
   template < typename U >
   unique_tuple_impl& operator=(const U& u)
   noexcept(std::is_nothrow_assignable<base_t,const U&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   template < typename U >
   unique_tuple_impl& operator=(U&& u)
   noexcept(std::is_nothrow_assignable<base_t,U&&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   // equaility compare
   template < typename U >
   constexpr bool operator==(const U& u) const noexcept
   {
      return static_cast<const base_t&>(*this) == u;
   }

   // swap
   template < typename U >
   void swap(U& u)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(u)))
   {
      static_cast<base_t&>(*this).swap(u);
   }
};

// general case
template < typename T, typename... U >
struct unique_tuple_impl<T,U...>
 : unique_tuple_helper<T,U...>::template eval<>::type
{
   // get
   template < typename V >
   friend typename std::enable_if<
      is_element_of<V,unique_tuple<T,U...>>::value,
      V&
   >::type
   constexpr get(unique_tuple_impl& t) noexcept
   {
      return get<V>(static_cast<unique_tuple_impl<V>&>(t));
   }

   // const get
   template < typename V >
   friend typename std::enable_if<
      is_element_of<V,unique_tuple<T,U...>>::value,
      const V&
   >::type
   constexpr get(const unique_tuple_impl& t) noexcept
   {
      return get<V>(static_cast<const unique_tuple_impl<V>&>(t));
   }

   // move get
   template < typename V >
   friend typename std::enable_if<
      is_element_of<V,unique_tuple<T,U...>>::value,
      V&&
   >::type
   constexpr get(unique_tuple_impl&& t) noexcept
   {
      return std::move(get<V>(static_cast<unique_tuple_impl<V>&&>(t)));
   }
   
   // constructor
   using base_t = typename unique_tuple_helper<T,U...>::template eval<>::type;
#if !defined(__GNUC__) || defined(__clang__)
   using base_t::base_t;
#else
   template < typename... V >
   explicit constexpr unique_tuple_impl(V&&... v)
   noexcept(std::is_nothrow_constructible<base_t,V&&...>::value)
    : base_t(std::forward<V>(v)...)
   {}
#endif

   // assign
   template < typename V >
   unique_tuple_impl& operator=(const V& v)
   noexcept(std::is_nothrow_assignable<base_t,const V&>::value)
   {
      static_cast<base_t&>(*this) = v;
      return *this;
   }

   template < typename V >
   unique_tuple_impl& operator=(V&& v)
   noexcept(std::is_nothrow_assignable<base_t,V&&>::value)
   {
      static_cast<base_t&>(*this) = v;
      return *this;
   }

   // equality compare
   template < typename V >
   constexpr bool operator==(const V& v) const noexcept
   {
      return static_cast<const base_t&>(*this) == v;
   }

   // swap
   template < typename V >
   void swap(V& v)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(v)))
   {
      static_cast<base_t&>(*this).swap(v);
   }
};

// unique tuple
template <>
struct unique_tuple<> {};

template < typename... T >
struct unique_tuple
 : private unique_tuple_impl<T...>
{
private:
   using base_t = unique_tuple_impl<T...>;
   
public:
   // constructor
   template<
      typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(U&&... u)
   noexcept(std::is_nothrow_constructible<base_t,U&&...>::value)
    : base_t(std::forward<U>(u)...)
   {}

   template<
      typename Alloc, typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(std::allocator_arg_t, const Alloc& a, U&&... u)
    : base_t(std::allocator_arg_t(),a,std::forward<U>(u)...)
   {}

   // construct from tuple
   template<
      typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(const std::tuple<U...>& u)
   noexcept(std::is_nothrow_constructible<base_t,const U&...>::value)
    : base_t(get<U>(u)...)
   {}

   template<
      typename Alloc, typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(std::allocator_arg_t, const Alloc& a, const std::tuple<U...>& u)
   noexcept(std::is_nothrow_constructible<base_t,const U&...>::value)
    : base_t(std::allocator_arg_t(),a,get<U>(u)...)
   {}

   template<
      typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(std::tuple<U...>&& u)
   noexcept(std::is_nothrow_constructible<base_t,U&&...>::value)
    : base_t(std::move(get<U>(u))...)
   {}

   template<
      typename Alloc, typename... U,
      typename=typename std::enable_if<
         is_subset_of<
            unique_tuple<__uncvref<U>...>,
            unique_tuple
         >::value
      >::type
   >
   explicit constexpr unique_tuple(std::allocator_arg_t, const Alloc& a, std::tuple<U...>&& u)
   noexcept(std::is_nothrow_constructible<base_t,U&&...>::value)
    : base_t(std::allocator_arg_t(),a,std::move(get<U>(u))...)
   {}
   
   // get
   template < typename U >
   friend typename std::enable_if<
      is_element_of<U,unique_tuple>::value,
      U&
   >::type
   constexpr get(unique_tuple& t) noexcept
   {
      return get<U>(static_cast<base_t&>(t));
   }

   // const get
   template < typename U >
   friend typename std::enable_if<
      is_element_of<U,unique_tuple>::value,
      const U&
   >::type
   constexpr get(const unique_tuple& t) noexcept
   {
      return get<U>(static_cast<const base_t&>(t));
   }

   // move get
   template < typename U >
   friend typename std::enable_if<
      is_element_of<U,unique_tuple>::value,
      U&&
   >::type
   constexpr get(unique_tuple&& t) noexcept
   {
      return std::move(get<U>(static_cast<base_t&&>(t)));
   }

   // assign
   template <
      typename U,
      typename=typename std::enable_if<
         is_subset_of<unique_tuple,unique_tuple<U>>::value
      >::type
   >
   unique_tuple& operator=(const U& u)
   noexcept(std::is_nothrow_assignable<base_t,const U&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   template <
      typename U,
      typename=typename std::enable_if<
         is_subset_of<unique_tuple,unique_tuple<U>>::value
      >::type
   >
   unique_tuple& operator=(U&& u)
   noexcept(std::is_nothrow_assignable<base_t,U&&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   // assign from tuple
   template <
      typename... U,
      typename=typename std::enable_if<
         is_subset_of<unique_tuple,unique_tuple<U...>>::value
      >::type
   >
   unique_tuple& operator=(const std::tuple<U...>& u)
   noexcept(std::is_nothrow_assignable<base_t,const std::tuple<U...>&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   template <
      typename... U,
      typename=typename std::enable_if<
         is_subset_of<unique_tuple,unique_tuple<U...>>::value
      >::type
   >
   unique_tuple& operator=(std::tuple<U...>&& u)
   noexcept(std::is_nothrow_assignable<base_t,std::tuple<U...>&&>::value)
   {
      static_cast<base_t&>(*this) = u;
      return *this;
   }

   // equality compare
   template < typename... U >
   friend
   typename std::enable_if<
      is_subset_of<unique_tuple,unique_tuple<U...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple>::value,
      bool
   >::type
   constexpr operator==(const unique_tuple& t, const unique_tuple<U...>& v) noexcept
   {
      return static_cast<const base_t&>(t) == v;
   }
   
   // equality compare with std::tuple
   template < typename... U >
   friend
   typename std::enable_if<
      is_subset_of<unique_tuple,unique_tuple<U...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple>::value,
      bool
   >::type
   constexpr operator==(const unique_tuple& t, const std::tuple<U...>& v) noexcept
   {
      return static_cast<const base_t&>(t) == v;
   }
   
   // swap
   template < typename... U >
   typename std::enable_if<
      is_subset_of<unique_tuple,unique_tuple<U...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple>::value
   >::type
   swap(unique_tuple<U...>& u)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(u)))
   {
      static_cast<base_t&>(*this).swap(u);
   }

   template < typename... U >
   friend
   typename std::enable_if<
      is_subset_of<unique_tuple,unique_tuple<U...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple>::value
   >::type
   swap(unique_tuple& t, unique_tuple<U...>& u)
   noexcept(noexcept(t.swap(u)))
   {
      t.swap(u);
   }

   // swap with std::tuple
   template < typename... U >
   typename std::enable_if<
      is_subset_of<unique_tuple,unique_tuple<U...>>::value
      && is_subset_of<unique_tuple<U...>,unique_tuple>::value
   >::type
   swap(std::tuple<U...>& u)
   noexcept(noexcept(static_cast<base_t&>(*this).swap(u)))
   {
      static_cast<base_t&>(*this).swap(u);
   }
};

// equality compare
template < typename... T, typename... U >
typename std::enable_if<
   !is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   || !is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value,
   bool
>::type
constexpr operator==(const unique_tuple<T...>&, const unique_tuple<U...>&) noexcept
{
   return false;
}

template < typename... T, typename... U >
constexpr bool operator!=(const unique_tuple<T...>& t, const unique_tuple<U...>& u) noexcept
{
   return !(t==u);
}

template < typename... T, typename... U >
typename std::enable_if<
   !is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   || !is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value,
   bool
>::type
constexpr operator==(const unique_tuple<T...>&, const std::tuple<U...>&) noexcept
{
   return false;
}

template < typename... T, typename... U >
constexpr bool operator!=(const unique_tuple<T...>& t, const std::tuple<U...>& u) noexcept
{
   return !(t==u);
}

template < typename... T, typename... U >
typename std::enable_if<
   is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   && is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value,
   bool
>::type
constexpr operator==(const std::tuple<U...>& v, const unique_tuple<T...>& t) noexcept
{
   return t==v;
}

template < typename... T, typename... U >
typename std::enable_if<
   !is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   || !is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value,
   bool
>::type
constexpr operator==(const std::tuple<U...>&, const unique_tuple<T...>&) noexcept
{
   return false;
}

template < typename... T, typename... U >
constexpr bool operator!=(const std::tuple<U...>& u, const unique_tuple<T...>& t) noexcept
{
   return t!=u;
}

// swap
template < typename... T, typename... U >
typename std::enable_if<
   is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   && is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value
>::type
swap(unique_tuple<T...>& t, std::tuple<U...>& u)
noexcept(noexcept(t.swap(u)))
{
   t.swap(u);
}

template < typename... T, typename... U >
typename std::enable_if<
   is_subset_of<unique_tuple<T...>,unique_tuple<U...>>::value
   && is_subset_of<unique_tuple<U...>,unique_tuple<T...>>::value
>::type
swap(std::tuple<T...>& u, unique_tuple<U...>& t)
noexcept(noexcept(swap(t,u)))
{
   swap(t,u);
}
